﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace WoWConnecter
{
    public delegate void AuthPacketMethod(AuthPacketIn packet);
    public delegate void RealmPacketMethod(RealmPacketIn packet);

    public class PacketHandler
    {
        private Dictionary<int, PacketMethod> mAuthHandlers;
        private Dictionary<int, PacketMethod> mRealmHandlers;

        public PacketHandler()
        {
            if (mAuthHandlers == null)
            {
                mAuthHandlers = new Dictionary<int, PacketMethod>();
                mRealmHandlers = new Dictionary<int, PacketMethod>();

                Load(Assembly.GetExecutingAssembly());
            }
        }

        public void Load(object obj)
        {
            Type type = obj.GetType();

            foreach (MethodInfo method in type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
            {
                PacketMethod[] packetMethods = (PacketMethod[])method.GetCustomAttributes(typeof(PacketMethod), false);

                ParameterInfo[] paramInfo = method.GetParameters();

                if (paramInfo.Length == 1)
                {
                    if (packetMethods.Length > 0)
                    {
                        object objMethod = null;
                        if (paramInfo[0].ParameterType == typeof(AuthPacketIn))
                            objMethod = Delegate.CreateDelegate(typeof(AuthPacketMethod), obj, method);
                        else if (paramInfo[0].ParameterType == typeof(RealmPacketIn))
                            objMethod = Delegate.CreateDelegate(typeof(RealmPacketMethod), obj, method);

                        foreach (PacketMethod handler in packetMethods)
                        {
                            handler.Method = objMethod;
                            handler.Object = obj;
                            handler.Type = GetMethodPacketType(objMethod);
                            Add(handler.PacketId, handler);
                        }
                    }
                }
            }
        }

        //We no longer use static methods
        //public void Load(Assembly assembly)
        //{
        //    foreach (Type type in assembly.GetTypes())
        //    {
        //        foreach (MethodInfo method in type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static))
        //        {
        //            PacketMethod[] packetMethods = (PacketMethod[])method.GetCustomAttributes(typeof(PacketHandler), false);

        //            ParameterInfo[] paramInfo = method.GetParameters();

        //            if (paramInfo.Length == 2)
        //            {
        //                if (packetMethods.Length > 0)
        //                {
        //                    AuthPacketMethod authMethod = null;
        //                    RealmPacketMethod realmMethod = null;

        //                    if (paramInfo[1].ParameterType == typeof(AuthPacketIn))
        //                        authMethod = (AuthPacketMethod)Delegate.CreateDelegate(typeof(AuthPacketMethod), method);
        //                    else if (paramInfo[1].ParameterType == typeof(RealmPacketIn))
        //                        realmMethod = (RealmPacketMethod)Delegate.CreateDelegate(typeof(RealmPacketMethod), method);

        //                    foreach (PacketMethod handler in packetMethods)
        //                    {
        //                        if (authMethod != null)
        //                            Add(handler.PacketId, handler);
        //                        else if (realmMethod != null)
        //                            Add(handler.PacketId, handler);
        //                    }
        //                }
        //            }
        //        }
        //    }
        //}

        public void Add(int packetId, PacketMethod packet)
        {
            if (packet.Type == PacketType.Realm)
            {
                mRealmHandlers.Remove(packet.PacketId);
                mRealmHandlers.Add(packet.PacketId, packet);
            }
            else
            {
                mAuthHandlers.Remove(packet.PacketId);
                mAuthHandlers.Add(packet.PacketId, packet);
            }
        }

        public void Handle(BasicPacketIn packet)
        {
            switch (packet.PacketType)
            {
                case PacketType.Authentication:
                    {
                        if (mAuthHandlers.ContainsKey(packet.RawId))
                        {
                            mAuthHandlers[packet.RawId].Invoke(packet as AuthPacketIn);
                        }
                    } break;
                case PacketType.Realm:
                    {
                        if (mRealmHandlers.ContainsKey(packet.RawId))
                        {
                            mRealmHandlers[packet.RawId].Invoke(packet as RealmPacketIn);
                        }
                        else
                        {
                            HandleUnhandledPacket(packet as RealmPacketIn);
                        }
                    } break;
            }
        }

        internal void HandleUnhandledPacket(RealmPacketIn packet)
        {
            Console.WriteLine();
        }

        public PacketType GetMethodPacketType(object method)
        {
            if (method is RealmPacketMethod)
                return PacketType.Realm;
            else
                return PacketType.Authentication;
        }

        public PacketMethod GetMethod(BasicPacketIn packet)
        {
            if (packet is RealmPacketIn)
            {
                if (mRealmHandlers.ContainsKey(packet.RawId))
                    return mRealmHandlers[packet.RawId];
            }
            else if (packet is AuthPacketIn)
            {
                if (mAuthHandlers.ContainsKey(packet.RawId))
                    return mAuthHandlers[packet.RawId];
            }

            return null;
        }

        public bool IsHandledIngame(RealmPacketIn packet)
        {
            PacketMethod method = GetMethod(packet);

            if (method != null && method.HandledIngame)
                return true;

            return false;
        }
    }
}
